package com.wosiliujing.learning.user.service.impl;

import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.util.StrUtil;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.wosiliujing.learning.pojo.dto.UserDTO;
import com.wosiliujing.learning.pojo.dto.UserInfo;
import com.wosiliujing.learning.pojo.dto.UserRestPasswordDTO;
import com.wosiliujing.learning.resources.entity.SysResources;
import com.wosiliujing.learning.role.entity.SysRoleResources;
import com.wosiliujing.learning.user.entity.SysUser;
import com.wosiliujing.learning.user.entity.SysUserRole;
import com.wosiliujing.learning.pojo.vo.UserVO;
import com.wosiliujing.learning.constant.CommonConstants;
import com.wosiliujing.learning.entity.SecurityUser;
import com.wosiliujing.learning.user.mapper.SysUserMapper;
import com.wosiliujing.learning.resources.service.SysResourcesService;
import com.wosiliujing.learning.role.service.SysRoleResourcesService;
import com.wosiliujing.learning.user.service.SysUserRoleService;
import com.wosiliujing.learning.user.service.SysUserService;
import com.wosiliujing.learning.util.RestResult;
import com.wosiliujing.learning.util.SecurityUtil;
import lombok.AllArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.BeanUtils;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.time.LocalDateTime;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.stream.Collectors;

/**
 * @author liujing
 */
@Slf4j
@AllArgsConstructor
@Service
@Transactional(rollbackFor = Exception.class)
public class SysUserServiceImpl  extends ServiceImpl<SysUserMapper,SysUser> implements SysUserService {

    private static final PasswordEncoder ENCODER = new BCryptPasswordEncoder();

    private final SysUserRoleService sysUserRoleService;

    private final SysRoleResourcesService sysRoleResourcesService;

    private final SysResourcesService sysResourcesService;


    @Override
    public UserInfo getUserInfo(SysUser sysUser) {
        UserInfo userInfo = new UserInfo();
        userInfo.setSysUser(sysUser);
        List<String> roles  = this.sysUserRoleService.list(Wrappers.<SysUserRole>query()
            .lambda().eq(SysUserRole::getUserId,sysUser.getUserId()))
                .stream()
                .map(SysUserRole::getRoleId)
                .collect(Collectors.toList());
        Set<String> permissions = new HashSet<>();
        roles.forEach(roleId ->{
            List<String> resources = this.sysRoleResourcesService.list(Wrappers.<SysRoleResources>query()
            .lambda().eq(SysRoleResources::getRoleId,roleId))
                    .stream()
                    .map(SysRoleResources::getResourcesId)
                    .collect(Collectors.toList());
            if(CollectionUtil.isNotEmpty(resources)){
                List<String> rolePermission = sysResourcesService.listByIds(resources)
                        .stream()
                        .filter(sysResources -> StrUtil.isNotBlank(sysResources.getPermission()))
                        .map(SysResources::getPermission)
                        .collect(Collectors.toList());
                permissions.addAll(rolePermission);
            }
        });
        userInfo.setPermission(permissions);
        return userInfo;
    }

    @Override
    public UserVO getUserVoById(String id) {
        return baseMapper.getUserVo(Wrappers.query().eq("sys_user.user_id",id).eq("sys_user.del_flag",CommonConstants.NORMAL_STATUS));
    }

    @Override
    public IPage getUserVoPage(Page page, UserDTO userDto) {
        QueryWrapper<SysUser> wrappers = Wrappers.query();
        if(StrUtil.isNotBlank(userDto.getUserName())){
            wrappers = wrappers.like("sys_user.user_name",userDto.getUserName());
        }
        wrappers=wrappers.orderByAsc("sys_user.create_time").eq("sys_user.del_flag",CommonConstants.NORMAL_STATUS);
        return baseMapper.getUserVoPage(page,wrappers);
    }

    @Override
    public Boolean saveUser(UserDTO userDto) {
        SysUser sysUser = new SysUser();
        BeanUtils.copyProperties(userDto, sysUser);
        sysUser.setPassword(ENCODER.encode(CommonConstants.INIT_PASSWORD));
        sysUser.setDelFlag(CommonConstants.NORMAL_STATUS);
        sysUser.setCreateTime(LocalDateTime.now());
        baseMapper.insert(sysUser);
        sysUserRoleService.saveBatch(userDto.getRoles(),sysUser.getUserId());
       return Boolean.TRUE;
    }

    @Override
    public Boolean deleteUser(String userId) {
        sysUserRoleService.remove(new QueryWrapper<SysUserRole>().lambda().eq(SysUserRole::getUserId,userId));
        return this.removeById(userId);
    }

    @Override
    public Boolean updateUser(UserDTO userDto) {
        sysUserRoleService.remove(new QueryWrapper<SysUserRole>().lambda().eq(SysUserRole::getUserId,userDto.getUserId()));
        SysUser sysUser = baseMapper.selectById(userDto.getUserId());
        sysUserRoleService.saveBatch(userDto.getRoles(),sysUser.getUserId());
        BeanUtils.copyProperties(userDto ,sysUser);
        sysUser.setUpdateTime(LocalDateTime.now());
        return this.updateById(sysUser);
    }

    @Override
    public RestResult restPassword(UserRestPasswordDTO userRestPasswordDto) {
        SysUser sysUser = baseMapper.selectById(userRestPasswordDto.getUserId());
        if (StrUtil.isNotBlank(userRestPasswordDto.getOldPassword())
                && StrUtil.isNotBlank(userRestPasswordDto.getNewPassword())) {
            if (ENCODER.matches(userRestPasswordDto.getOldPassword(), sysUser.getPassword())) {
                sysUser.setPassword(ENCODER.encode(userRestPasswordDto.getNewPassword()));
            } else {
                log.warn("原密码错误，修改密码失败:{}", sysUser.getUserName());
                return new RestResult<>(Boolean.FALSE, "原密码错误，修改失败");
            }
        }
        baseMapper.updateById(sysUser);
        return RestResult.ok();
    }

    @Override
    public RestResult restOwnPassword(UserRestPasswordDTO userRestPasswordDto) {
        SecurityUser user = SecurityUtil.getUser();
        assert user != null;
        SysUser sysUser = this.getOne(
                Wrappers.<SysUser>lambdaQuery().eq(SysUser::getUserName,user.getUsername()));
        if (StrUtil.isNotBlank(userRestPasswordDto.getOldPassword())
                && StrUtil.isNotBlank(userRestPasswordDto.getNewPassword())) {
            if (ENCODER.matches(userRestPasswordDto.getOldPassword(), sysUser.getPassword())) {
                sysUser.setPassword(ENCODER.encode(userRestPasswordDto.getNewPassword()));
            } else {
                log.warn("修改密码失败:{},原密码错误",sysUser.getUserName());
                return new RestResult<>(Boolean.FALSE, "原密码错误，修改失败");
            }
        }
        baseMapper.updateById(sysUser);
        return RestResult.ok();
    }

    @Override
    public Boolean updateOwnInfo(UserDTO userDto) {
        SecurityUser user = SecurityUtil.getUser();
        assert user != null;
        SysUser sysUser = this.getOne(
                Wrappers.<SysUser>lambdaQuery().eq(SysUser::getUserName,user.getUsername()));
        sysUser.setPhone(userDto.getPhone());
        return this.updateById(sysUser);
    }

}
